/**
 * 对 axios 的二次封装
 *
 * 特性:
 * 对axios 原本的请求配置做了扩展, 增加了两个属性
 * interceptors: {requestFulfilled(config): config , requestReject(err): err},
 * showLoading: boolean // 默认true
 *
 * 功能:
 *
 * 将拦截器(响应拦截和请求拦截)做分级处理
 * 分别支持全局拦截器\实例拦截器\业务方法拦截器
 * 全局拦截器已经在类中定义好主要负责请求loading状态的管理
 * 实例拦截器可以根据接口需求选择是否携带token
 * const request = new JRequest({
 *  baseURL: '/api',
 *  timeout: 1000 * 60 * 5,
 *  interceptors: {
 *    responseFulfilled: (config) => {
 *      console.log('实例的response拦截')
 *      return config
 *    },
 *    requestFulfilled: (res) => {
 *      console.log('实例的request拦截')
 *      return res
 *    }
 *  }
 *})
 * 业务方法拦截器可以对接口返回的特殊数据结构做处理(这个场景是我瞎想的, 嘻嘻)
 * export const login = data => {
 *   return request.post({
 *     url: '/login',
 *     data,
 *     interceptors: {
 *       responseFulfilled: (config) => {
 *         console.log('业务方法的response拦截')
 *         return config
 *       },
 *       requestFulfilled: (res) => {
 *         console.log('业务方法的request拦截')
 *         return res
 *       }
 *     }
 *   })
 * }
 *
 * 其中全局拦截器的loading可以通过showLoading选项配置是否显示,
 * 如果创建实例的时候showLoading设置为false,
 * 则通过该实例所发出的请求, 页面都不会有loading效果
 *
 * 暴露了常用的请求方法,
 * get(config): Promise
 * post(config): Promise
 * delete(config): Promise
 * patch(config): Promise
 */

import axios from 'axios'
import { useLoadingStore } from '@/store/loading'
import errorTip from './utils'

const DEFAULT_LOADING = true

class JRequest {
  constructor(config) {
    this.instance = axios.create(config)
    this.interceptors = config.interceptors
    this.showLoading = config.showLoading ?? DEFAULT_LOADING
    // 全局的请求拦截
    this.instance.interceptors.request.use(
      (config) => {
        const loadingStore = useLoadingStore()
        if (this.showLoading) {
          loadingStore.loading = true
        }
        return config
      },
      (err) => {
        return err
      }
    )

    // 实例的请求拦截
    this.instance.interceptors.request.use(
      this.interceptors?.requestFulfilled,
      this.interceptors?.requestReject
    )

    // 全局的响应拦截
    this.instance.interceptors.response.use(
      (res) => {
        const loadingStore = useLoadingStore()
        loadingStore.loading = false
        if (res.config.responseType === 'blob') {
          return res
        }
        if (res.data.code !== 200) {
          errorTip(res.data.message)
          return Promise.reject(res.data.message)
        }
        return res.data
      },
      (err) => {
        const loadingStore = useLoadingStore()
        loadingStore.loading = false
        errorTip(err.response.data?.message || err.response.statusText)
        return Promise.reject(err)
      }
    )

    // 实例的响应拦截
    this.instance.interceptors.response.use(
      this.interceptors?.responseFulfilled,
      this.interceptors?.responseReject
    )
  }
  request(config) {
    return new Promise((resolve, reject) => {
      // 业务方法的请求拦截
      if (config.interceptors?.requestFulfilled) {
        config = config.interceptors?.requestFulfilled(config)
      }
      // 和默认的loading开关值相反时才需要赋值
      if (config.showLoading === !this.showLoading) {
        this.showLoading = config.showLoading ?? DEFAULT_LOADING
      } else {
        this.showLoading = config.showLoading ?? DEFAULT_LOADING
      }

      this.instance
        .request(config)
        .then((res) => {
          // 业务方法的响应拦截
          if (config.interceptors?.responseFulfilled) {
            res = config.interceptors?.responseFulfilled(res)
          }
          resolve(res)
        })
        .catch((err) => {
          reject(err)
        })
    })
  }
  get(config) {
    return this.request({ ...config, method: 'GET' })
  }
  post(config) {
    return this.request({ ...config, method: 'POST' })
  }
  delete(config) {
    return this.request({ ...config, method: 'DELETE' })
  }
  patch(config) {
    return this.request({ ...config, method: 'PATCH' })
  }
}

export default JRequest
